﻿ // csbuf 协议解释器
enum buftype {
    buftype_unknow			= 0,
    buftype_bool			= 1,
    buftype_uint8			= 1,
    buftype_uint16			= 2,
    buftype_uint32			= 4,
    buftype_uint64			= 8,
    buftype_int8			= 1,
    buftype_int16			= 2,
    buftype_int32			= 4,
    buftype_int64			= 8,
    buftype_float			= 4,
    buftype_double			= 8,
    buftype_string_l        = 253,  // 长字符串
    buftype_string_s		= 254,
    buftype_string			= 255,
}

export default class csbufparser {
    data: ArrayBuffer;
    view: DataView;
    cursor: number;
    length: number;
    posStack: number[];

    useAutoInt32: true;

    constructor(num: number = 65535) {
        this.data = new ArrayBuffer(num);
        this.view = new DataView(this.data);
        this.cursor = 0;
        this.length = 0;
        this.posStack = [];
    }

    private stringToByte(str) {
        var bytes = new Array();
        var len, c;
        len = str.length;
        for (var i = 0; i < len; i++) {
            c = str.charCodeAt(i);
            if (c >= 0x010000 && c <= 0x10FFFF) {
                bytes.push(((c >> 18) & 0x07) | 0xF0);
                bytes.push(((c >> 12) & 0x3F) | 0x80);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000800 && c <= 0x00FFFF) {
                bytes.push(((c >> 12) & 0x0F) | 0xE0);
                bytes.push(((c >> 6) & 0x3F) | 0x80);
                bytes.push((c & 0x3F) | 0x80);
            } else if (c >= 0x000080 && c <= 0x0007FF) {
                bytes.push(((c >> 6) & 0x1F) | 0xC0);
                bytes.push((c & 0x3F) | 0x80);
            } else {
                bytes.push(c & 0xFF);
            }
        }
        return bytes;


    }


    private byteToString(arr) {
        if (typeof arr === 'string') {
            return arr;
        }
        var str = '',
            _arr = arr;
        for (var i = 0; i < _arr.length; i++) {
            var one = _arr[i].toString(2),
                v = one.match(/^1+?(?=0)/);
            if (v && one.length == 8) {
                var bytesLength = v[0].length;
                var store = _arr[i].toString(2).slice(7 - bytesLength);
                for (var st = 1; st < bytesLength; st++) {
                    store += _arr[st + i].toString(2).slice(2);
                }
                str += String.fromCharCode(parseInt(store, 2));
                i += bytesLength - 1;
            } else {
                str += String.fromCharCode(_arr[i]);
            }
        }
        return str;
    }

    setData(data: string) {
        this.length = data.length;
        for (let i = 0; i < data.length; i++) {
            this.view.setUint8(i, data.charCodeAt(i));
        }
        this.cursor = 0;
    }

    setData2(data: Uint8Array) {
        this.length = data.length;
        // for (let i = 0; i < data.length; i++) {
        //     this.view.setUint8(i, data[i]);
        // }
        let uint8Data = new Uint8Array(this.data);
        uint8Data.set(data, 0);
        this.cursor = 0;
    }

    getData() {
        return String.fromCharCode.apply(null, new Uint8Array(this.data, 0, this.length));
    }

    clear() {
        this.cursor = 0;
        this.length = 0;
        this.posStack = [];
    }

    setBool(d: boolean, pos: number) {
        let b: number = 0;
        if (d) b = 1;
        else if (0 == pos) return;
        this.view.setInt8(this.cursor, pos);
        this.view.setInt8(this.cursor + 1, buftype.buftype_bool);
        this.view.setInt8(this.cursor + 2, b);
        this.cursor = this.cursor + 3;
        this.length = this.cursor;
    }

    setUint8(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_uint8);
        this.view.setUint8(this.cursor + 2, d);
        this.cursor = this.cursor + 3;
        this.length = this.cursor;
    }

    setUint16(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_uint16);
        this.view.setUint16(this.cursor + 2, d, false);
        this.cursor = this.cursor + 4;
        this.length = this.cursor;
    }

    setUint32(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        if (this.useAutoInt32 && d <= 255) {
            this.setUint8(d, pos);
        } else if (this.useAutoInt32 && d <= 65535) {
            this.setUint16(d, pos);
        } else {
            this.view.setUint8(this.cursor, pos);
            this.view.setUint8(this.cursor + 1, buftype.buftype_uint32);
            this.view.setUint32(this.cursor + 2, d, false);
            this.cursor = this.cursor + 6;
            this.length = this.cursor;
        }
    }

    // js 没有64位整形，当成32位来处理了
    setUint64(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_uint64);
        this.view.setUint32(this.cursor + 2, (d >>> 31) >>> 1, false);
        this.view.setUint32(this.cursor + 6, d, false);
        this.cursor = this.cursor + 10;
        this.length = this.cursor;
    }

    setInt8(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_int8);
        this.view.setInt8(this.cursor + 2, d);
        this.cursor = this.cursor + 3;
        this.length = this.cursor;
    }

    setInt16(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_int16);
        this.view.setInt16(this.cursor + 2, d, false);
        this.cursor = this.cursor + 4;
        this.length = this.cursor;
    }

    setInt32(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        if (this.useAutoInt32 && d >= -128 && d <= 127) {
            this.setInt8(d, pos);
        } else if (this.useAutoInt32 && d >= -32768 && d <= 32767) {
            this.setInt16(d, pos);
        } else {
            this.view.setUint8(this.cursor, pos);
            this.view.setUint8(this.cursor + 1, buftype.buftype_int32);
            this.view.setInt32(this.cursor + 2, d, false);
            this.cursor = this.cursor + 6;
            this.length = this.cursor;
        }
    }

    // js 没有64位整形，当成32位来处理了
    setInt64(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_int64);
        this.view.setInt32(this.cursor + 2, (d >>> 31) >>> 1, false);
        this.view.setInt32(this.cursor + 6, d, false);
        this.cursor = this.cursor + 10;
        this.length = this.cursor;
    }

    setFloat(d: number, pos: number) {
        if (d > -0.0000001 && d < 0.0000001 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_float);
        this.view.setFloat32(this.cursor + 2, d, false);
        this.cursor = this.cursor + 6;
        this.length = this.cursor;
    }

    setDouble(d: number, pos: number) {
        if (d > -0.0000001 && d < 0.0000001 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_double);
        this.view.setFloat64(this.cursor + 2, d, false);
        this.cursor = this.cursor + 10;
        this.length = this.cursor;
    }

    setString(d: string, pos: number) {
        if (d.length == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        let data = this.stringToByte(d);
        if (data.length <= 255) {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string_s);
            this.view.setUint8(this.cursor + 2, data.length);
            for (let i = 0; i < data.length; i++) {
                this.view.setUint8(this.cursor + 3 + i, data[i]);
            }
            this.cursor = this.cursor + 3 + data.length
        } else if (data.length <= 65535) {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string);
            this.view.setUint16(this.cursor + 2, data.length, false);
            for (let i = 0; i < data.length; i++) {
                this.view.setUint8(this.cursor + 4 + i, data[i]);
            }
            this.cursor = this.cursor + 4 + data.length;
        } else {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string_l);
            this.view.setUint32(this.cursor + 2, data.length, false);
            for (let i = 0; i < data.length; i++) {
                this.view.setUint8(this.cursor + 6 + i, data[i]);
            }
            this.cursor = this.cursor + 6 + data.length;
        }
        this.length = this.cursor
    }

    setBytes(d: number[], len: number, pos: number) {
        if (d.length == 0 && 0 != pos) return;
        this.view.setUint8(this.cursor, pos);
        if (d.length <= 255) {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string_s);
            this.view.setUint8(this.cursor + 2, d.length);
            for (let i = 0; i < d.length; i++) {
                this.view.setUint8(this.cursor + 3 + i, d[i]);
            }
            this.cursor = this.cursor + 3 + d.length
        } else if (d.length <= 65535) {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string);
            this.view.setUint16(this.cursor + 2, d.length, false);
            for (let i = 0; i < d.length; i++) {
                this.view.setUint8(this.cursor + 4 + i, d[i]);
            }
            this.cursor = this.cursor + 4 + d.length;
        } else {
            this.view.setUint8(this.cursor + 1, buftype.buftype_string_l);
            this.view.setUint32(this.cursor + 2, d.length, false);
            for (let i = 0; i < d.length; i++) {
                this.view.setUint8(this.cursor + 6 + i, d[i]);
            }
            this.cursor = this.cursor + 6 + d.length;
        }
        this.length = this.cursor
    }

    setArray(d: number, pos: number) {
        if (d == 0 && 0 != pos) return;
        if (d <= 255) {
            this.setUint8(d, pos);
        } else {
            this.setUint16(d, pos);
        }
    }

    setMessage(pos: number) {
        this.view.setUint8(this.cursor, pos);
        this.view.setUint8(this.cursor + 1, buftype.buftype_string);
        this.view.setUint8(this.cursor + 2, 0);
        this.view.setUint8(this.cursor + 3, 0);
        this.cursor += 4;
        this.length = this.length;
        this.posStack.push(this.cursor)
    }

    endSetMessage() {
        if (this.posStack.length > 0) {
            let pos = this.posStack.pop();
            let len = this.length - pos;
            if (len > 0 && len <= 65535) {
                this.view.setUint16(pos - 2, len, false);
            } else if (len > 65535) {
                this.cursor += 2;
                this.length = this.cursor;
                for (let i = this.length - 1; i >= pos + 2; --i) {
                    this.view.setUint8(i, this.view.getUint8(i - 2));
                }
                this.view.setUint32(pos - 2, len, false);
                this.view.setUint8(pos - 3, buftype.buftype_string_l);
            } else if (this.view.getUint8(pos - 4) != 0) {
                this.cursor -= 4;
                this.length = this.cursor;
            }
        }
    }

    getBool(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 2 < this.length) {
                this.cursor += 3;
                return this.view.getInt8(this.cursor - 1) == 1;
            }
        }
        return false;
    }

    getUint8(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 2 < this.length) {
                this.cursor += 3;
                return this.view.getUint8(this.cursor - 1);
            }
        }
        return 0;
    }

    getUint16(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 3 < this.length) {
                this.cursor += 4;
                return this.view.getUint16(this.cursor - 2, false);
            }
        }
        return 0;
    }

    getUint32(pos: number) {
        if (this.setCursorWithPos(pos)) {
            let n = this.view.getUint8(this.cursor + 1)
            if (1 == n) {
                if (this.cursor + 2 < this.length) {
                    this.cursor += 3;
                    return this.view.getUint8(this.cursor - 1);
                }
            } else if (2 == n) {
                if (this.cursor + 3 < this.length) {
                    this.cursor += 4;
                    return this.view.getUint16(this.cursor - 2, false);
                }
            } else if (4 == n) {
                if (this.cursor + 5 < this.length) {
                    this.cursor += 6;
                    return this.view.getUint32(this.cursor - 4, false);
                }
            }
        }
        return 0;
    }

    // js 没有64位整形，当成32位来处理了
    getUint64(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 9 < this.length) {
                this.cursor += 10;
                let h = this.view.getUint32(this.cursor - 8, false);
                return (h << 32) + this.view.getUint32(this.cursor - 4, false);
            }
        }
        return 0;
    }

    getInt8(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 2 < this.length) {
                this.cursor += 3;
                return this.view.getInt8(this.cursor - 1);
            }
        }
        return 0;
    }

    getInt16(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 3 < this.length) {
                this.cursor += 4;
                return this.view.getInt16(this.cursor - 2, false);
            }
        }
        return 0;
    }

    getInt32(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 1 < this.length) {
                let n = this.view.getUint8(this.cursor + 1)
                if (1 == n) {
                    if (this.cursor + 2 < this.length) {
                        this.cursor += 3;
                        return this.view.getInt8(this.cursor - 1);
                    }
                } else if (2 == n) {
                    if (this.cursor + 3 < this.length) {
                        this.cursor += 4;
                        return this.view.getInt16(this.cursor - 2, false);
                    }
                } else if (4 == n) {
                    if (this.cursor + 5 < this.length) {
                        this.cursor += 6;
                        return this.view.getInt32(this.cursor - 4, false);
                    }
                }
            }
        }
        return 0;
    }

    // js 没有64位整形，当成32位来处理了
    getInt64(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 9 < this.length) {
                this.cursor += 10;
                let h = this.view.getInt32(this.cursor - 8, false);
                return (h << 32) + this.view.getInt32(this.cursor - 4, false);
            }
        }
        return 0;
    }

    getFloat(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 5 < this.length) {
                this.cursor += 6;
                return this.view.getFloat32(this.cursor - 4, false);
            }
        }
        return 0;
    }

    getDouble(pos: number) {
        if (this.setCursorWithPos(pos)) {
            if (this.cursor + 9 < this.length) {
                this.cursor += 10;
                return this.view.getFloat64(this.cursor - 8, false);
            }
        }
        return 0;
    }

    getString(pos: number) {
        if (this.setCursorWithPos(pos)) {
            let s = this.getSize()
            if (this.cursor + 3 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string) {
                this.cursor += 2;
                let len = this.view.getUint16(this.cursor, false);
                this.cursor += 2;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return this.byteToString(new Uint8Array(this.data, this.cursor - len, len));
                }
                this.cursor += len;
            } else if (this.cursor + 2 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string_s) {
                this.cursor += 2;
                let len = this.view.getUint8(this.cursor);
                this.cursor += 1;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return this.byteToString(new Uint8Array(this.data, this.cursor - len, len));
                }
                this.cursor += len;
            } else if (this.cursor + 5 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string_l) {
                this.cursor += 2;
                let len = this.view.getUint32(this.cursor, false);
                this.cursor += 4;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return this.byteToString(new Uint8Array(this.data, this.cursor - len, len));
                }
                this.cursor += len;
            }
        }
        return "";
    }

    getBytes(pos: number) {
        if (this.setCursorWithPos(pos)) {
            let s = this.getSize()
            if (this.cursor + 3 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string) {
                this.cursor += 2;
                let len = this.view.getUint16(this.cursor, false);
                this.cursor += 2;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return new Uint8Array(this.data, this.cursor - len, len);
                }
                this.cursor += len;
            } else if (this.cursor + 2 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string_s) {
                this.cursor += 2;
                let len = this.view.getUint8(this.cursor);
                this.cursor += 1;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return new Uint8Array(this.data, this.cursor - len, len);
                }
                this.cursor += len;
            } else if (this.cursor + 5 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string_l) {
                this.cursor += 2;
                let len = this.view.getUint32(this.cursor, false);
                this.cursor += 4;
                if (this.cursor + len <= s) {
                    this.cursor += len;
                    return new Uint8Array(this.data, this.cursor - len, len);
                }
                this.cursor += len;
            }
        }
        return [];
    }

    getArray(pos: number) {
        if (this.setCursorWithPos(pos)) {
            let s = this.getSize();
            if (this.cursor + 1 < s) {
                if (this.view.getUint8(this.cursor + 1) == buftype.buftype_uint8) {
                    if (this.cursor + 2 < s) {
                        this.cursor += 3;
                        return this.view.getUint8(this.cursor - 1);
                    }
                } else if (this.view.getUint8(this.cursor + 1) == buftype.buftype_uint16) {
                    if (this.cursor + 3 < s) {
                        this.cursor += 4;
                        return this.view.getUint16(this.cursor - 2);
                    }
                }
            }
        }
        // return this.getUint8(pos);
        return 0;
    }

    getMessage(pos: number) {
        if (this.setCursorWithPos(pos)) {
            let s = this.getSize();
            if (this.cursor + 3 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string) {
                this.cursor += 2;
                let len = this.view.getUint16(this.cursor, false);
                this.cursor += 2;
                this.posStack.push(this.cursor + len);
            } else if (this.cursor + 5 < s && this.view.getUint8(this.cursor + 1) == buftype.buftype_string_l) {
                this.cursor += 2;
                let len = this.view.getUint32(this.cursor, false);
                this.cursor += 4;
                this.posStack.push(this.cursor + len);
            } else {
                this.posStack.push(0);
            }
        } else {
            this.posStack.push(this.cursor);
        }
        return 0;
    }

    endGetMessage() {
        if (this.posStack.length > 0) {
            this.cursor = this.posStack.pop();
        }
    }


    setCursorWithPos(pos: number) {
        let s = this.getSize();
        if (this.cursor + 1 < s) {
            let p: number = this.view.getUint8(this.cursor);
            if (p == pos) return true;
            else if (p < pos) {
                if (buftype.buftype_string == this.view.getUint8(this.cursor + 1)) {
                    this.cursor += 2;
                    if (this.cursor + 2 >= s) return false;
                    let len = this.view.getUint16(this.cursor, false);
                    this.cursor += 2 + len;
                } else if (buftype.buftype_string_s == this.view.getUint8(this.cursor + 1)) {
                    this.cursor += 2;
                    if (this.cursor + 2 >= s) return false;
                    let len = this.view.getUint8(this.cursor);
                    this.cursor += 1 + len;
                } else if (buftype.buftype_string_l == this.view.getUint8(this.cursor + 1)) {
                    this.cursor += 2;
                    if (this.cursor + 2 >= s)   return false;
                    let len = this.view.getUint32(this.cursor, false);
                    this.cursor += 4 + len;
                } else {
                    this.cursor += 2 + this.view.getUint8(this.cursor + 1);
                }
                return this.setCursorWithPos(pos);
            }
        }
        return false;
    }

    getSize() {
        if (this.posStack.length > 0) {
            return this.posStack[this.posStack.length - 1];
        }
        return this.length;
    }
}